!theme mono

skinparam classAttributeIconSize 0
skinparam classFontSize 12
skinparam classAttributeFontSize 11

title HealthSync 역설계 - User Service 데이터 설계서

package "User Service Database Schema" as user_db #lightgreen {
  
  entity "users" as user {
    * member_serial_number : BIGINT <<PK>>
    --
    * google_id : VARCHAR(255) <<UK>>
    * name : VARCHAR(100)
    * birth_date : DATE
    * occupation : VARCHAR(50) <<FK>>
    * created_at : TIMESTAMP
    * updated_at : TIMESTAMP
    * last_login_at : TIMESTAMP
    --
    + 인덱스: idx_google_id
    + 인덱스: idx_occupation
    + 인덱스: idx_created_at
  }

  entity "occupation_types" as occupation_type {
    * occupation_code : VARCHAR(20) <<PK>>
    --
    * occupation_name : VARCHAR(100)
    * category : VARCHAR(50)
    * is_active : BOOLEAN
    * created_at : TIMESTAMP
    * updated_at : TIMESTAMP
    --
    + 인덱스: idx_occupation_name
    + 인덱스: idx_category
  }
  
  note right of user
    **사용자 테이블 특징**
    • Google OAuth 기반 인증
    • member_serial_number는 
      건강보험공단 데이터 연동 키
    • occupation은 직업 코드 참조
    • 소프트 삭제 미적용 (물리 삭제)
  end note
  
  note right of occupation_type
    **직업 유형 테이블 특징**
    • 표준 직업 분류 기반
    • 카테고리별 그룹핑
    • 활성/비활성 상태 관리
    • 마스터 데이터 성격
  end note
}

package "관계 정의" as relationships {
  user ||--o{ occupation_type : occupation
  
  note as n1
    **외래키 관계**
    users.occupation → occupation_types.occupation_code
    
    **참조 무결성**
    • CASCADE 업데이트
    • RESTRICT 삭제 (직업 유형 삭제 시 제한)
    
    **데이터 정합성**
    • occupation 필드는 NULL 허용 (프로필 미완성 상태)
    • birth_date는 NOT NULL (필수 정보)
    • google_id는 UNIQUE 제약 (중복 가입 방지)
  end note
}

package "데이터 타입 및 제약조건" as constraints {
  note as n2
    **users 테이블 제약조건**
    • member_serial_number: AUTO_INCREMENT
    • google_id: UNIQUE, NOT NULL
    • name: NOT NULL, 최대 100자
    • birth_date: NOT NULL, 1900-01-01 이후
    • occupation: NULL 허용, 최대 50자
    • created_at: DEFAULT CURRENT_TIMESTAMP
    • updated_at: ON UPDATE CURRENT_TIMESTAMP
    • last_login_at: NULL 허용
    
    **occupation_types 테이블 제약조건**
    • occupation_code: PRIMARY KEY, 최대 20자
    • occupation_name: UNIQUE, NOT NULL, 최대 100자
    • category: NOT NULL, 최대 50자
    • is_active: DEFAULT TRUE
    • created_at: DEFAULT CURRENT_TIMESTAMP
    • updated_at: ON UPDATE CURRENT_TIMESTAMP
  end note
}

package "초기 데이터" as initial_data {
  note as n3
    **occupation_types 초기 데이터 예시**
    
    | occupation_code | occupation_name | category |
    |----------------|----------------|----------|
    | OFF001 | 사무직 | 사무관리 |
    | MFG001 | 제조업 생산직 | 제조생산 |
    | SVC001 | 서비스업 | 서비스 |
    | EDU001 | 교육직 | 전문직 |
    | MED001 | 의료진 | 전문직 |
    | IT001 | IT개발자 | 전문직 |
    | SAL001 | 영업직 | 영업마케팅 |
    | DRV001 | 운전직 | 운송 |
    | CON001 | 건설업 | 건설 |
    | FRE001 | 프리랜서 | 기타 |
    
    **카테고리 분류**
    • 사무관리: 앉아서 일하는 직종
    • 제조생산: 신체 활동이 많은 직종  
    • 서비스: 고객 응대 직종
    • 전문직: 전문 지식 요구 직종
    • 영업마케팅: 외부 활동 직종
    • 운송: 이동이 많은 직종
    • 건설: 육체 노동 직종
    • 기타: 분류하기 어려운 직종
  end note
}

package "성능 및 운영 고려사항" as performance {
  note as n4
    **인덱스 전략**
    • users.google_id: 로그인 시 빠른 조회
    • users.occupation: 직업별 통계 조회
    • users.created_at: 가입 일자별 조회
    • occupation_types.occupation_name: 직업명 검색
    
    **파티셔닝**
    • 현재 단계에서는 미적용
    • 사용자 수 증가 시 created_at 기준 월별 파티셔닝 고려
    
    **백업 및 복구**
    • users: 매일 풀백업 (개인정보 보호)
    • occupation_types: 주간 백업 (마스터 데이터)
    
    **모니터링 지표**
    • 신규 가입자 수 (일별/월별)
    • 활성 사용자 수 (last_login_at 기준)
    • 직업별 사용자 분포
    • 평균 프로필 완성률
  end note
}

package "데이터 보안" as security {
  note as n5
    **개인정보 보호**
    • name: 암호화 저장 고려 (실명)
    • birth_date: 날짜 정보 마스킹
    • google_id: 해시 처리 또는 암호화
    
    **접근 제어**
    • 개인정보 조회 시 로그 기록
    • 관리자 접근 시 승인 프로세스
    • API 호출 시 사용자 본인 확인
    
    **데이터 보존**
    • 회원 탈퇴 시 개인정보 즉시 삭제
    • 로그인 기록 90일 보존
    • 감사 로그 1년 보존
    
    **GDPR/개인정보보호법 준수**
    • 데이터 처리 목적 명시
    • 동의 철회 시 데이터 삭제
    • 데이터 이동권 지원
  end note
}